
/****  BIBLIOTECAS Y ESTRUCTURAS  ***/

#include "Ppd_funciones.h"
#include "Algoritmos.h"
#include "Estructuras.h"
#include "TADComunicacion.h"
#include <stdlib.h>
#include "log.h"



/********** VARIABLES GLOBALES ***********/

pistaSector infoCabActual;
TNodoPista *listaPistas, *listaPistasEncolar;
int32_t cantidadSectoresPorPista;
int32_t* CHS;
sem_t Semaforo;
int32_t SockConsola,contador=0;
int32_t sectoresEnTrace;
int32_t ord; //Para el algoritmo SSTF
char algoritmo[10];
char movimientoCabezal;
int32_t tLectura,tEscritura,tEntrePistas;
float tEntreSectores;
pthread_mutex_t mutex,mutexMap;
int32_t cantPistas;
int32_t flagCambio;
char PORT[50];
int32_t flagPri,flagTRACE,sectoresEnClean;;
char rutaDisco[50];
char rutaEjecutableDisco[50];
char rutaArchivoConfig[256];
char *map;
int32_t cantSectoresSincronizados=-1,archivo,contador2=0,contador3=0,opcionLog;
int32_t flagSincronizacion;
int32_t tamanioDisco;
int32_t flagSalida;
t_log *log;



void *FuncionProcesar();


int32_t main(int32_t argv, char *argc[]){

	int32_t sockUnix,puertoRaid,valConnect=-1;
	int32_t Listener,longUnix,fdmax=0;
	int32_t SockRaid,flagModo=0,n,i,valHand,addrlen,newfd=0,var=0,varC=1;
	int32_t *ipRaid;
	int32_t RPM,ii=0;
	pid_t pid;
	flagSalida=0;
	char *ModoInicio,IP_CONNECT[20],*nombreDisco;
	struct sockaddr_in their_addr;
	struct sockaddr_in remoteaddr;
	struct sockaddr* puntSockUnix;
	struct sockaddr_un *dirUnixClient;
	fd_set read_fds;
	VecSockets vector[100];
	pthread_t threadID;
	pthread_mutex_init(&mutex,NULL);
	sem_init(&Semaforo,0,0);
	//pthread_mutex_init(&mutexMap,NULL);
	log = log_create("PPD", "logPPD.txt", DEBUG | INFO | WARNING, M_CONSOLE_ENABLE);

	if((argv!=2)){
		printf("Error en los parametros.\n");
		log_warning(log, "PPD", "Message warning: %s", "Error en los parametros.");
		return 1;
	}

	signal(SIGCHLD,sigchld_handler);

	SockConsola=-1;
	flagSalida=0;
	flagTRACE=0;
	listaPistas=NULL;
	listaPistasEncolar=NULL;

	//Inicializo vector de Sockets
	for(n=0;n<10;n++){
		vector[n].Sock=-1;
	}

	memset(rutaArchivoConfig,'\0',sizeof(rutaArchivoConfig));
	memcpy(rutaArchivoConfig,argc[1],strlen(argc[1]));
	
	funRutaEjecutableDisco();
	rutaDelDisco();
	obtenerAlgoritmo();
	ModoInicio=obtenerModoInicio();

	infoCabActual.pista=obtenerPistaActual();
	infoCabActual.sector=obtenerSectorActual();

	tLectura=obtenerTiempoLectura();
	tEscritura=obtenerTiempoEscritura();
	CHS=obtenerCHS();
	cantidadSectoresPorPista = CHS[2];
	cantPistas=CHS[0];
	free(CHS);

	opcionLog=obtenerFlagLog();

	obtenerPuertoConsola();
	movimientoCabezal=obtenerMovimientoCabezal();

	RPM=obtenerRPM();	
	tEntreSectores=(float)((60+0.0)/RPM)/cantidadSectoresPorPista;
	tEntreSectores=tEntreSectores*1000;//pasamos de segundos a milisegundos
	tEntrePistas= obtenerTiempoEntrePistas();

	log_info(log, "PPD", "Message info: %s %s", "Algoritmo:", algoritmo);

	char *args[] = {"/Consola",rutaArchivoConfig, NULL };
	pid=fork();
	/*******************  Consola *******************/
	if(pid== 0){
		execv(rutaEjecutableDisco, args);
		perror("Error en execv()");
	}
	/*********************  PPD *********************/
	else{
		if(pid<0){
			perror("Error en fork()\n");
		}
		else{
			flagSincronizacion=0;
			leerDisco();

			sockUnix= ServUnix();

			longUnix =sizeof(struct sockaddr_un);
			puntSockUnix =( struct sockaddr* ) &dirUnixClient;
			SockConsola = (int32_t)accept( sockUnix, (struct sockaddr *)puntSockUnix, (socklen_t *)&longUnix );
			fdmax=SockConsola;
			varC=1;

			if(!strcmp(ModoInicio,"CONNECT")){
				log_info(log, "PPD", "Message info: %s", "ModoInicio: CONNECT.");
				ipRaid = obtenerIpRaid();
				puertoRaid =obtenerPuertoRaid();
				SockRaid = (int32_t)socket(AF_INET, SOCK_STREAM, 0);
				their_addr.sin_family = AF_INET;
				their_addr.sin_port = htons(puertoRaid);
				sprintf(IP_CONNECT,"%d.%d.%d.%d",ipRaid[0],ipRaid[1],ipRaid[2],ipRaid[3]);
				free(ipRaid);
				their_addr.sin_addr.s_addr = inet_addr(IP_CONNECT);
				memset(&(their_addr.sin_zero),'\0', 8);
				valConnect=connect(SockRaid, (struct sockaddr *)&their_addr, sizeof(struct sockaddr));

				if(valConnect==-1){
					printf("\nRaid desconectado.\n");
					log_warning(log, "PPD", "Message warning: %s", "Raid desconectado.");
					close(SockConsola);
					free(log);
					msync(map, tamanioDisco, MS_ASYNC);
					munmap(map,tamanioDisco);
					close(archivo);
					close(SockRaid);
					char llamada[100];
					memset(llamada,'\0',sizeof(llamada));
					sprintf(llamada,"kill -9 %d",pid);
					system(llamada);
					printf("\n##Proceso PPD terminado.##\n");
					exit(EXIT_FAILURE);
				}
				else{
					printf("\nRaid conectado.\n");
					var=1;
					log_info(log, "PPD", "Message info: %s", "Raid conectado.");
					valHand=HandShakeEmisor(SockRaid,1);

					if(flagSincronizacion)
						cantSectoresSincronizados=(cantidadSectoresPorPista*cantPistas)+1;

					if(valHand==1){
						nombreDisco=obtenerNombreDisco();
						Nombre(SockRaid,nombreDisco,'P');
						free(nombreDisco);

						log_info(log, "PPD", "Message info: %s", "Hicimos HandShake Correctamente");
						flagModo=1;
						if(SockConsola > SockRaid)
							fdmax = SockConsola;
						else
							fdmax = SockRaid;
					}
					else{
						printf("\nFallo el HandShake.\n");
						log_warning(log, "PPD", "Message warning: %s", "Fallo el HandShake.");
						close(SockConsola);
						free(log);
						msync(map, tamanioDisco, MS_ASYNC);
						munmap(map,tamanioDisco);
						close(archivo);
						close(SockRaid);
						char llamada[100];
						memset(llamada,'\0',sizeof(llamada));
						sprintf(llamada,"kill -9 %d",pid);
						system(llamada);
						printf("\n##Proceso PPD terminado.##\n");
						exit(EXIT_FAILURE);
					}
				}
			}

			if(!strcmp(ModoInicio,"LISTEN")){
				log_info(log, "PPD", "Message info: %s", "ModoInicio: LISTEN.");
				Listener = CListen();
				flagModo=2;
				if(SockConsola > Listener)
					fdmax = SockConsola;
				else
					fdmax = Listener;
			}
			free(ModoInicio);


			//Thread para procesar la lista de peticiones
			pthread_create(&threadID, NULL, FuncionProcesar,0);

			while(flagSalida==0){
				FD_ZERO(&read_fds);
				FD_SET(SockConsola, &read_fds);

				if(flagModo==1){
					if(var)
						FD_SET(SockRaid, &read_fds);
				}
				if(flagModo==2)
					FD_SET(Listener, &read_fds);


				for(i=0;i<10;i++){

					if(vector[i].Sock > fdmax){
						fdmax = vector[i].Sock;
					}
					if(vector[i].Sock!=-1)
						FD_SET(vector[i].Sock, &read_fds);

				}

				if(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1){
					printf("\nFallo el Select\n");
					log_error(log, "PPD", "Message error: %s", "Fallo el Select");
				}

				for(i=0; i <= fdmax; i++){

					if(FD_ISSET(i, &read_fds)){

						if(flagModo==1){
							if(i==SockRaid){
								var=EncolarPeticion(SockRaid);
								if(var==0 || var==-1){
									flagSalida=1;
									close(SockRaid);
								}
							}
							if(i==SockConsola){
								varC=FuncionConsola(SockConsola);
								if(varC==0 || varC==-1){
									flagSalida=1;
									close(SockConsola);
								}
							}
						}

						if(flagModo==2){
							if(i==Listener){
								addrlen = sizeof(remoteaddr);
								newfd=0;
								while(newfd<=0){
									newfd = (int32_t)accept(Listener, (struct sockaddr*)&remoteaddr, (socklen_t *)&addrlen);
								}
								if(newfd > fdmax){
									fdmax = newfd;
								}
								ii=0;
								for(ii=0;ii<10;ii++){
									if(vector[ii].Sock==-1){
										vector[ii].Sock=newfd;
										break;
									}
								}
								HandShakeReceptor(newfd,0,0,0);
								ObtenerNombre(newfd);
							}
							if(i==SockConsola){
								varC=FuncionConsola(SockConsola);
								if(varC==0 || varC==-1){
									flagSalida=1;
									close(SockConsola);
								}
							}


							if((i!=SockConsola)&&(i!=Listener)){
								var=EncolarPeticion(i);
								if(var==0){
									for(ii=0;ii<10;ii++){
										if(vector[ii].Sock==i){
											vector[ii].Sock=-1;
											break;
										}
									}
								}
							}
						}

						if(flagModo==0){
							varC= FuncionConsola(SockConsola);
							if(varC==0 || varC==-1){
								flagSalida=1;
								close(SockConsola);
							}
						}

					}
				}
			}
		}
		if(var==0 || var==-1){
			char llamada[100];
			memset(llamada,'\0',sizeof(llamada));
			sprintf(llamada,"kill -9 %d",pid);
			system(llamada);
		}
	}
	
	//se cerro por el Raid
	if(var==0 || var==-1){
		close(SockConsola);
	}
	
	//se cerro por la consola
	if(varC==0 || varC==-1){
		if(flagModo==1)
			close(SockRaid);
			
		if(flagModo==2){
			for(ii=0;ii<10;ii++){
				if(vector[ii].Sock!=-1){
					close(vector[ii].Sock);
				}
			}
		}
	}
	
	//pthread_mutex_lock(&mutexMap);
	msync(map, tamanioDisco, MS_SYNC);
	munmap(map,tamanioDisco);
	//pthread_mutex_unlock(&mutexMap);

	close(archivo);
	fclose(log->file);
	free(log);

	TNodoPista *auxiliar=NULL;
	auxiliar=listaPistas;
	while(listaPistas){
		auxiliar=listaPistas;
		listaPistas=listaPistas->Next;
		free(auxiliar);
	}

	TNodoPista *auxiliar2=NULL;
	auxiliar2=listaPistasEncolar;
	while(listaPistas){
		auxiliar2=listaPistasEncolar;
		listaPistasEncolar=listaPistasEncolar->Next;
		free(auxiliar2);
	}

	//pthread_exit(&threadID);
	//pthread_exit(&threadID2);
	//pthread_mutex_destroy(&mutex);
	printf("\n##Proceso PPD terminado.##\n");
	return 0;
}


/*** Thread para procesar la lista de peticiones ***/
void *FuncionProcesar(){
	TNodoPista *pistaAtender;
	TNodoPedido *nodoPedido;
	char sectores[500000],*info,*cadena;
	int32_t cantPistasAtravesadas,cantSectoresAtravesados,cantMensajes,modT,largoCadena,a,b,c;
	float tTotalPisas,tTotalSectores;
	TMensaje *miMensaje;
	char *parteTrace;
	pistaSector proxSectorTRACE;
	extern int32_t SockConsola;
	int32_t sectorLogico;
	char pedidos[500000];
	char *datos,*contenido;
	float tTotalPistas;

	flagPri=0;
	while(flagSalida==0){

		sem_wait(&Semaforo);
		if((listaPistas)||(listaPistasEncolar))
		{
		if(!memcmp(algoritmo,"FSCAN",strlen("FSCAN"))){

			pthread_mutex_lock(&mutex);
			//sleep(2);
			listaPistas=listaPistasEncolar;
			listaPistasEncolar=NULL;
			pthread_mutex_unlock(&mutex);
		}

		while((listaPistas!=NULL) && (listaPistas->SL!=NULL)){

			pthread_mutex_lock(&mutex);
			pistaAtender=buscarProximaPista();
			pthread_mutex_unlock(&mutex);
			if(flagPri==1 && !strcmp(algoritmo,"FSCAN")){

				if(movimientoCabezal=='A'&& pistaAtender->pista<infoCabActual.pista)
					movimientoCabezal='D';

				if(movimientoCabezal=='D'&& pistaAtender->pista>infoCabActual.pista)
					movimientoCabezal='A';
			}

			while((pistaAtender!=NULL)&&(pistaAtender->SL!=NULL)){

				if(opcionLog==1)
					enumerarPedidos(pedidos,pistaAtender);

				nodoPedido=buscarProxSector(pistaAtender,&proxSectorTRACE);

				switch(nodoPedido->tOper){

				case 'E':
					if(opcionLog==1){

						datos=(char*)calloc(1,sizeof(char)*12000);
						memset(datos,'\0',12000);

						sprintf(datos,"\n%sPosicion actual:  %d:%d \n",pedidos,infoCabActual.pista,infoCabActual.sector);

						if(!strcmp(algoritmo,"SSTF"))
							obtenerSectoresRecorridosSSTF(nodoPedido,infoCabActual,sectores,&cantPistasAtravesadas, &cantSectoresAtravesados);
						if(!strcmp(algoritmo,"FSCAN"))
							obtenerSectoresRecorridosFSCAN(nodoPedido,infoCabActual,sectores,&cantPistasAtravesadas, &cantSectoresAtravesados);

						tTotalPistas=cantPistasAtravesadas*tEntrePistas;
						tTotalSectores=cantSectoresAtravesados*tEntreSectores;
						sprintf(datos,"%sSector solicitado:  %d:%d\n",datos,nodoPedido->pista,nodoPedido->sector);
						sprintf(datos,"%sSectores recorridos:  %s\n",datos,sectores);
						sprintf(datos,"%sTiempo consumido:  %.2f ms\n",datos,tTotalSectores+tTotalPistas);
						if(proxSectorTRACE.pista==-1)
							sprintf(datos,"%sProximo sector:  ...\n",datos);
						else
							sprintf(datos,"%sProximo sector:  %d:%d",datos,proxSectorTRACE.pista,proxSectorTRACE.sector);

						log_info(log, "PPD", "Message info: %s", datos);
						free(datos);
					}

					if((flagSincronizacion)&&(cantSectoresSincronizados>0))
						cantSectoresSincronizados--;

					sectorLogico=cantidadSectoresPorPista*nodoPedido->pista+nodoPedido->sector;

					escribirSector(sectorLogico,nodoPedido->datos);


					if(nodoPedido->sock!=SockConsola){
						if(!flagSincronizacion)
							ComunicacionA(nodoPedido->sock,'P',"Ok",nodoPedido->idDescriptor);
					}
					else{
						if(sectoresEnClean==1){
							ComunicacionA(nodoPedido->sock,'P',"Ok",nodoPedido->idDescriptor);
						}
						else
							sectoresEnClean--;
					}
					break;



				case 'L':
					if(opcionLog==1){

						datos=(char*)calloc(1,sizeof(char)*12000);

						memset(datos,'\0',12000);
						sprintf(datos,"\n%sPosicion actual:  %d:%d \n",pedidos,infoCabActual.pista,infoCabActual.sector);

						if(!strcmp(algoritmo,"SSTF"))
							obtenerSectoresRecorridosSSTF(nodoPedido,infoCabActual,sectores,&cantPistasAtravesadas, &cantSectoresAtravesados);
						if(!strcmp(algoritmo,"FSCAN"))
							obtenerSectoresRecorridosFSCAN(nodoPedido,infoCabActual,sectores,&cantPistasAtravesadas, &cantSectoresAtravesados);

						
						tTotalPistas=cantPistasAtravesadas*tEntrePistas;
						tTotalSectores=cantSectoresAtravesados*tEntreSectores;

						sprintf(datos,"%sSector solicitado:  %d:%d\n",datos,nodoPedido->pista,nodoPedido->sector);
						sprintf(datos,"%sSectores recorridos:  %s\n",datos,sectores);
						sprintf(datos,"%sTiempo consumido:  %.2f ms\n",datos,tTotalSectores+tTotalPistas);
						if(proxSectorTRACE.pista==-1)
							sprintf(datos,"%sProximo sector:  ...\n",datos);
						else
							sprintf(datos,"%sProximo sector:  %d:%d",datos,proxSectorTRACE.pista,proxSectorTRACE.sector);

						log_info(log, "PPD", "Message info: %s", datos);
						free(datos);
					}
					sectorLogico=cantidadSectoresPorPista*nodoPedido->pista+nodoPedido->sector;
					contenido=(char*)calloc(1,512);
					leerSector(sectorLogico,contenido);

					ComunicacionA(nodoPedido->sock,'P',contenido,nodoPedido->idDescriptor);

					//contador3++;
					//if(contador3==10000){
							//printf("Envie el sector %d\n",sectorLogico);
						//	contador3=0;
					//}

					free(contenido);
					break;



				case 'T':
					sectoresEnTrace--;
					modT=-1;
					//modificar como te devuelve sectores(300) la siguiente funcion:
					if(!strcmp(algoritmo,"SSTF"))
						obtenerSectoresRecorridosSSTF(nodoPedido,infoCabActual,sectores,&cantPistasAtravesadas, &cantSectoresAtravesados);
					if(!strcmp(algoritmo,"FSCAN"))
						obtenerSectoresRecorridosFSCAN(nodoPedido,infoCabActual,sectores,&cantPistasAtravesadas, &cantSectoresAtravesados);

					//Verificamos movimiento.
					if(flagCambio==1 && (!strcmp(algoritmo,"FSCAN")) && flagPri!=1){
						if(movimientoCabezal=='A')
							movimientoCabezal='D';
						else
							movimientoCabezal='A';
					}
					tTotalPisas=cantPistasAtravesadas*tEntrePistas;
					tTotalSectores=cantSectoresAtravesados*tEntreSectores;
					if(flagTRACE==0){
						cadena=malloc(800000);
						memset(cadena,'\0',800000);
					}
					info=(char *)calloc(1,900000);
					memset(info,'\0',900000);

					sprintf(info,"%s\n\nPosicion actual:  %d:%d \n",info,infoCabActual.pista,infoCabActual.sector); // cadena tiene la cadena TOTAL a enviar a la consola
					sprintf(info,"%sSector solicitado:  %d:%d\n",info,nodoPedido->pista,nodoPedido->sector);
					sprintf(info,"%sSectores recorridos:  %s\n",info,sectores);

					//sprintf(info,"%scantSectoresAtravesados:  %d\n",info,cantSectoresAtravesados);
					//sprintf(info,"%scantPistasAtravesadas:  %d\n",info,cantPistasAtravesadas);

					sprintf(info,"%sTiempo consumido:  %.2f ms\n",info,tTotalSectores+tTotalPisas);
					if(proxSectorTRACE.pista==-1)
						sprintf(info,"%sProximo sector:  ...\n",info);
					else
						sprintf(info,"%sProximo sector:  %d:%d",info,proxSectorTRACE.pista,proxSectorTRACE.sector);


					if(flagTRACE==0){
						sprintf(cadena,"%s",info);
						flagTRACE=1;
					}
					else
						sprintf(cadena,"%s%s",cadena,info);

					if(opcionLog==1){
						log_info(log, "PPD", "Message info: %s", info);
					}

					free(info);


					if(sectoresEnTrace==0){
						c=0;
						largoCadena=strlen(cadena);
						cantMensajes=largoCadena/1099;
						modT=largoCadena%1099;
						if(modT>0)
							cantMensajes++;
						miMensaje=(TMensaje* )calloc(1,sizeof(struct TMensaje));
						parteTrace=(char*)calloc(1,sizeof(char)*1100);
						a=0;
						memset(parteTrace,'\0',1100);
						sprintf(parteTrace,"%d",cantMensajes);
						memcpy(miMensaje->payload,parteTrace,1100);
						send(SockConsola,(void*)miMensaje,sizeof(struct TMensaje),0);
						while(c<cantMensajes){
							b=0;
							memset(parteTrace,'\0',1100);
							while(b<1099){
								parteTrace[b]=cadena[a];
								b++;
								a++;
							}
							memcpy(miMensaje->payload,parteTrace,1100);
							miMensaje->payloadLength=1100;
							memcpy(miMensaje->idDescriptor,nodoPedido->idDescriptor,16);
							send(SockConsola,(void*)miMensaje,sizeof(struct TMensaje),0);
							c++;
						}
						free(parteTrace);
						free(cadena);
						free(miMensaje);
						flagTRACE=0;//para ver cuando empieza y cuando termina el trace
					}
					break;
				}


				//ACTUALIZAMOS LA POSICION DEL CABEZAL
				if(nodoPedido->sector==cantidadSectoresPorPista-1)
					nodoPedido->sector=0;
				else
					nodoPedido->sector=nodoPedido->sector+1;

				infoCabActual.pista=nodoPedido->pista;
				infoCabActual.sector=nodoPedido->sector;

				free(nodoPedido);
				
			}

			free(pistaAtender);

		}
	}
	}
	return 0;
}